package solver;

import java.util.Random;

public class Redi {
	private static short[][] ctm = new short[360][4];
	private static byte[][] cpm = new byte[36][4];
	private static short[][] com = new short[2187][4];
	private static byte[] ctd = new byte[360];
	private static byte[][] cd = new byte[2187][36];
	private static Random r = new Random();

	private static boolean ini = false;
	public Redi() {
		if(!ini) {
			init();
			ini = true;
		}
	}

	private void init() {
		// move tables
		/* center
		 * 		  0
		 *	4	1	2	3
		 * 		  5
		 */
		int[] arr = new int[7];
		for (int i=0; i<360; i++)
			for (int j=0; j<4; j++) {
				Mapping.idxToEperm(arr, i, 6);
				switch(j) {
					case 0: Mapping.cir(arr, 2, 5, 3); break;	//R
					case 1: Mapping.cir(arr, 0, 3, 4); break;	//U
					case 2: Mapping.cir(arr, 1, 4, 5); break;	//L
					case 3: Mapping.cir(arr, 3, 5, 4); break;	//B
				}
				ctm[i][j] = (short) Mapping.epermToIdx(arr, 6);
			}
		/* corner permutation
		 *	0		1
		 *		3
		 *		2
		 *
		 *		0
		 *	1		2
		 */
		int[] arr2 = new int[3];
		for (int i=0; i<12; i++)
			for (int j=0; j<3; j++) {
				for (int k=0; k<4; k++) {
					Mapping.idxToEperm(arr, i, 4);
					Mapping.idxToEperm(arr2, j, 3);
					switch (k) {
						case 0: Mapping.cir(arr, 1, 2, 3); break;	//R
						case 1: Mapping.cir(arr, 0, 1, 3); break;	//U
						case 2: Mapping.cir(arr, 2, 0, 3); break;	//L
						case 3: Mapping.cir(arr2, 0, 2, 1); break;	//B
					}
					cpm[i*3+j][k] = (byte) (Mapping.epermToIdx(arr, 4)*3+Mapping.epermToIdx(arr2, 3));
				}
			}
		//corner orientation
		/*
		 *		0
		 *	1		2
		 *		3
		 *	4		5
		 *		6
		 */
		for(int i=0; i<2187; i++) {
			for(int j=0; j<4; j++) {
				Mapping.idxToOri(arr, i, 3, 7);
				switch (j) {
					case 0:
						Mapping.cir(arr, 2, 6, 3); arr[2]+=2; arr[3]+=2; arr[5]++; arr[6]+=2; break;
					case 1:
						Mapping.cir(arr, 1, 2, 3); arr[0]++; arr[1]+=2; arr[2]+=2; arr[3]+=2; break;
					case 2:
						Mapping.cir(arr, 1, 3, 6); arr[1]+=2; arr[3]+=2; arr[4]++; arr[6]+=2; break;
					case 3:
						Mapping.cir(arr, 0, 5, 4); arr[0]+=2; arr[3]++; arr[4]+=2; arr[5]+=2; break;
				}
				com[i][j] = (short) Mapping.oriToIdx(arr, 3, 7);
			}
		}

		// distance table
		for (int i = 0; i < 360; i++)
			ctd[i] = -1;
		ctd[0] = 0;
		//int c = 1;
		for(int depth = 0; depth < 5; depth++) {
			for (int i = 0; i < 360; i++)
				if (ctd[i] == depth)
					for (int m = 0; m < 4; m++) {
						int p = i;
						for(int n = 0; n < 2; n++){
							p = ctm[p][m];
							if (ctd[p] == -1) {
								ctd[p] = (byte) (depth + 1);
								//c++;
							}
						}
					}
			//System.out.println(depth+1+" "+c);
		}
		for(int i=0; i<2187; i++)
			for(int j=0; j<36; j++) cd[i][j] = -1;
		cd[0][0] = 0;
		//c=1;
		for(int d=0; d<7; d++) {
			for(int i=0; i<2187; i++)
				for(int j=0; j<36; j++)
					if(cd[i][j] == d)
						for(int k=0; k<4; k++) {
							int p = i, q = j;
							for(int l=0; l<2; l++) {
								p = com[p][k]; q = cpm[q][k];
								if(cd[p][q] == -1) {
									cd[p][q] = (byte)(d+1);
									//c++;
								}
							}
						}
			//System.out.println(d+1+" "+c);
		}
	}

	private static StringBuffer sol;
	private static String[] turn = {"R", "L"};
	private static String[] suff = {"'", ""};
	public String scramble() {
		init();
		sol = new StringBuffer();
		int lastScr;
		for (int i = 0; i < 8; i++) {
			int len = 2 + r.nextInt(4);
			lastScr = r.nextInt(2);
			sol.append(((0 == i) ? "" : " ") + turn[lastScr] + suff[r.nextInt(2)]);
			for (int d = 1; d < len; d++)
			{
				sol.append(" " + turn[1-lastScr] + suff[r.nextInt(2)]);
				lastScr = 1-lastScr;
			}
			sol.append(" x");
			if ( 1 == (i % 2) ) sol.append("\n");
		}
		return sol.toString();
	}

	public String scramble(int minLen) {
		init();
		sol = new StringBuffer();
		int lastScr;
		int scrLen = minLen;
		if ( scrLen < 8 ) scrLen = 8;
		for (int i = 0; i < scrLen; i++) {
			int len = 2 + r.nextInt(4);
			lastScr = r.nextInt(2);
			sol.append(((0 == i) ? "" : " ") + turn[lastScr] + suff[r.nextInt(2)]);
			for (int d = 1; d < len; d++)
			{
				sol.append(" " + turn[1-lastScr] + suff[r.nextInt(2)]);
				lastScr = 1-lastScr;
			}
			sol.append(" x\n");
		}
		return sol.toString();
	}

	/*
	 *				0	1	2
	 *				3		4
	 *				5	6	7
	 *	8	9	10	16	17	18	24	25	26	32	33	34
	 *	11		12	19		20	27		28	35		36
	 *	13	14	15	21	22	23	29	30	31	37	38	39
	 *				40	41	42
	 *				43		44
	 *				45	46	47
	 */
	private static byte[] img = new byte[48];
	private void initColor() {
		for (int i=0; i<8; i++)
			for (byte j=0; j<6; j++) img[j*8+i] = j;
	}
	private static void cir3(byte[] arr, int a, int b, int c) {
		byte temp = arr[a];
		arr[a] = arr[b];
		arr[b] = arr[c];
		arr[c] = temp;
	}
	private static void cir4(byte[] arr, int a, int b, int c, int d) {
		byte temp = arr[a];
		arr[a] = arr[b];
		arr[b] = arr[c];
		arr[c] = arr[d];
		arr[d] = temp;
	}
	private void move(int turn) {
		switch(turn) {
			case 0:	//R
				cir3(img, 2, 26, 32);
				cir3(img, 1, 25, 35);
				cir3(img, 33, 4, 28);
				break;
			case 1:	//L
				cir3(img, 0, 34, 8);
				cir3(img, 3, 33, 11);
				cir3(img, 9, 1, 36);
				break;
			case 2:	//x
				cir4(img, 5, 21, 45, 34);
				cir4(img, 16, 40, 39, 0);
				cir4(img, 10, 15, 13, 8);
				cir4(img, 7, 23, 47, 32);
				cir4(img, 18, 42, 37, 2);
				cir4(img, 24, 29, 31, 26);
				cir4(img, 6, 22, 46, 33);
				cir4(img, 17, 41, 38, 1);
				cir4(img, 9, 12, 14, 11);
				cir4(img, 3, 19, 43, 36);
				cir4(img, 25, 27, 30, 28);
				cir4(img, 4, 20, 44, 35);
				break;
		}
	}
	private static String moveIdx="RLx";
	public byte[] image(String scr) {
		initColor();
		String[] s = scr.replace("\n", " ").split(" ");
		for (int i=0; i<s.length; i++)
			if (s[i].length() > 0) {
				int mov = moveIdx.indexOf(s[i].charAt(0));
				move(mov);
				if(s[i].length() > 1) {
					move(mov);
					if (2 == mov)
					{
						move(mov);
					}
				}
			}
		return img;
	}
}
